﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel.Web;
using System.Threading;
using IdentityAtRest.Contracts;
using IdentityAtRest.Service.Authorization;
using IdentityAtRest.Utility.Identity;
using IdentityAtRest.Utility.Identity.Authorization;

namespace IdentityAtRest.Service
{
    /// <summary>
    /// 
    /// </summary>
    public class MonkeyShavingService : IMonkeyShavingService
    {
        private static readonly List<Monkey> _monkeys = new List<Monkey>();

        private readonly IAuthorizationManager _authManager;

        public MonkeyShavingService() : this(new ExpressionAuthorizationManager())
        {
        }

        //If you're using an IoC to create your service instance,
        //this signature may be useful
        public MonkeyShavingService(IAuthorizationManager auth)
        {
            _authManager = auth;
        }

       
        #region IMonkeyShavingService Members

        public Monkey PutMonkeyInShaver(Monkey monkey)
        {
            //Only authenticated users can add monkeys to the shaver
            if (AuthorizeFor(
                    Permissions.MonkeyShavingService.CanAddMonkeyToShaver) == false)
            {
                SetStatusCode(HttpStatusCode.Forbidden, "Only authenticated users can put monkeys in the shaver.");
                return null;
            }

            _monkeys.Add(monkey);
            ShaveMonkey(monkey);
            return monkey;
        }

        public List<Monkey> ListAllMonkeys()
        {
            //Only administrators can list all monkeys.
            //Everyone else can just list shaved monkeys.
            if (AuthorizeFor(
                    Permissions.MonkeyShavingService.CanViewUnshavedMonkeys) == false)
            {
                return ListMonkeysByStatus(MonkeyStatus.ShavedClean.ToString());
            }

            return _monkeys;
        }

        public List<Monkey> ListMonkeysByStatus(string status)
        {
            if (status != "ShavedClean"
                && (AuthorizeFor(Permissions.MonkeyShavingService.CanViewUnshavedMonkeys) == false))
            {
                SetStatusCode(HttpStatusCode.Forbidden, "Only administrators can list non-shaved monkeys.");
                return new List<Monkey>();
            }

            return
                _monkeys.Where(m => m.Status.ToString()
                                        .Equals(status)).ToList();
        }

        public Monkey GetMonkey(string monkeyName)
        {
            IEnumerable<Monkey> found =
                _monkeys.Where(m => m.Name.Equals(monkeyName, StringComparison.CurrentCultureIgnoreCase));
            if (found.Count() > 0)
            {
                Monkey m = found.First();
                if (m.Status != MonkeyStatus.ShavedClean &&
                    (AuthorizeFor(Permissions.MonkeyShavingService.CanViewUnshavedMonkeys) == false))
                {
                    SetStatusCode(HttpStatusCode.Forbidden, "Only administrators can view non-shaved monkeys.");
                    return null;
                }

                return m;
            }

            SetStatusCode(HttpStatusCode.NotFound, "A monkey with the name " + monkeyName + " could not be found.");
            return null;
        }

        public string BuyMonkeyABeer(string monkeyName)
        {
            //The call to GetMonkey will ensure that the service
            //only allows monkeys to be accessed for whome
            //the logged in user has permissions.
            Monkey monkey = GetMonkey(monkeyName);
            
            if (monkey == null)
                return null;


            if (AuthorizeForExpression("AgeInYears >= 21") == false)
            {
                SetStatusCode(HttpStatusCode.Unauthorized,
                              "Only users age 21 or older may buy a monkey an alcoholic beverage.");
                return null;
            }

            return
                String.Format(
                    "{0} thanks you for the beer, but don't think for a second you're going to get lucky...{0} isn't that kind of monkey.",
                    monkey.Name);


        }

        #endregion

        private static void ShaveMonkey(Monkey monkey)
        {
            var random = new Random();
            int val = random.Next(1, 4);
            if (val == 1)
            {
                monkey.Status = MonkeyStatus.Unshaven;
                monkey.Message = "We're sorry, your monkey is too big to fit in our shaver, and could not be shaved.";
            }
            else if (val == 2)
            {
                monkey.Status = MonkeyStatus.PartiallyShaved;
                monkey.Message =
                    "We're sorry, your monkey would not stay still long enough for the shaving process to complete.";
            }
            else
            {
                monkey.Status = MonkeyStatus.ShavedClean;
                monkey.Message =
                    "Contratulations - You're monkey was fully shaved by the MonkeyShaver service. A good time for a tattoo, but you better hurry, it grows back quickly!";
            }
        }

      
        private static void SetStatusCode(HttpStatusCode code, string description)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = code;
            WebOperationContext.Current.OutgoingResponse.StatusDescription = description;
        }

        private static IClaimsPrincipal CurrentUser
        {
            get { return Thread.CurrentPrincipal as IClaimsPrincipal; }
        }


        private bool AuthorizeFor(params string[] requestedPermissions)
        {
            if (CurrentUser == null)
                return false;

            return _authManager.IsAuthorized(CurrentUser.ClaimsIdentity.Claims, requestedPermissions);
        }

        private bool AuthorizeForExpression(string permissionPolicyExpression)
        {
            var auth = _authManager as ExpressionAuthorizationManager;
            if (auth == null || CurrentUser == null)
                return false;

            IList<IClaim> claims = CurrentUser.ClaimsIdentity.Claims;

            return auth.AuthorizePolicyExpression(claims, permissionPolicyExpression).IsAuthorized;
        }
    }
}